package com.system.common.service.purview.impl;

import com.system.common.bean.SnowflakeIdWorker;
import com.system.common.constant.BaseEnum;
import com.system.common.constant.PurTypeEnum;
import com.system.common.constant.RecordStatusConst;
import com.system.common.constant.SessionKeyConst;
import com.system.common.dao.purview.PurviewMapper;
import com.system.common.entity.purview.Purview;
import com.system.common.entity.purview.PurviewExample;
import com.system.common.request.BaseReq;
import com.system.common.request.purview.SavePurviewReq;
import com.system.common.response.BaseResp;
import com.system.common.response.PageResp;
import com.system.common.service.purview.PurviewService;
import com.system.common.service.role.RoleService;
import com.system.common.util.EntityUtil;
import com.system.common.vo.purview.MenuVo;
import com.system.common.vo.purview.PurviewVo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class PurviewServiceImpl implements PurviewService {

    private static final Logger logger = LoggerFactory.getLogger(PurviewServiceImpl.class);

    private static Set<String> NOT_LOGIN_PUR = new HashSet<>();
    private static Map<Long, Purview> ALL_PURVIEW = new LinkedHashMap<>();
    private static Map<Integer, Map<Long, Purview>> ALL_LEVEL_PURVIEW = new LinkedHashMap<>();

    @Autowired
    private PurviewMapper purviewMapper;

    @Autowired
    private SnowflakeIdWorker idWorker;

    @Override
    public BaseResp savePurview(SavePurviewReq req) {
        BaseResp resp = new BaseResp();
        if(req.getPurType()==null || req.getPurType()==0
                || StringUtils.isBlank(req.getPurName())){
            resp.setCode(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getCode());
            resp.setMsg(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getMsg());
            return resp;
        }
        PurTypeEnum purType = PurTypeEnum.getType(req.getPurType());
        if(purType==null){
            resp.setCode(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getCode());
            resp.setMsg("菜单类型错误");
            return resp;
        }

        if(purType != PurTypeEnum.MENU_URL){
            if(StringUtils.isBlank(req.getPurUrl())){
                resp.setCode(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getCode());
                resp.setMsg("权限url不能为空");
                return resp;
            }

        }

        if(req.getId()==null || req.getId()==0){
            // 添加
            Purview purview = new Purview();
            purview.setId(idWorker.nextId());
            purview.setPurName(req.getPurName());
            purview.setPurDescribe(req.getPurDescribe());
            purview.setPurUrl(req.getPurUrl());
            purview.setPurType(purType.getCode());
            purview.setLogin(req.getLogin());
            purview.setOrderNum(req.getOrderNum());
            purview.setState(RecordStatusConst.ENABLE);
            purview.setCreateTime(new Date());
            Long pid = 0L;
            Integer level = 1;
            if(req.getParentId()!=null && req.getParentId()!=0){
                Purview parent = getPurviewByPrimaryKey(req.getParentId());
                if(parent==null){
                    resp.setCode(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getCode());
                    resp.setMsg("父菜单不存在");
                    return resp;
                }
                pid = parent.getId();
                level = parent.getPurLevel()+1;
            }
            purview.setParentId(pid);
            purview.setPurLevel(level);
            purviewMapper.insertSelective(purview);
            cachePurview(purview);
        }else{
            // 更新
            if(req.getParentId()==null){
                resp.setCode(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getCode());
                resp.setMsg("父菜单不存在");
                return resp;
            }
            Long pid = 0L;
            Integer level = 1;
            if(req.getParentId()!=0){
                Purview parent = getPurviewByPrimaryKey(req.getParentId());
                if(parent==null){
                    if(purType != PurTypeEnum.API){
                        resp.setCode(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getCode());
                        resp.setMsg("父菜单不存在");
                        return resp;
                    }
                }else{
                    pid = parent.getId();
                    level = parent.getPurLevel()+1;
                }
            }

            Purview purview = new Purview();
            purview.setId(req.getId());
            purview.setParentId(pid);
            purview.setPurLevel(level);
            purview.setPurName(req.getPurName());
            purview.setPurDescribe(req.getPurDescribe());
            purview.setPurUrl(req.getPurUrl());
            purview.setPurType(purType.getCode());
            purview.setState(RecordStatusConst.ENABLE);
            purview.setLogin(req.getLogin());
            purview.setEditTime(new Date());
            purview.setOrderNum(req.getOrderNum());
            PurviewExample example = new PurviewExample();
            PurviewExample.Criteria criteria = example.createCriteria();
            criteria.andIdEqualTo(req.getId());
            purviewMapper.updateByExampleSelective(purview, example);
            cachePurview(purview);
        }
        resp.setCode(BaseEnum.SUCCESS.getCode());
        resp.setMsg("成功");
        return resp;
    }

    @Override
    public PageResp queryAllPurview(BaseReq req) {

        PageResp resp = new PageResp();

        MenuVo root = new MenuVo();
        root.setId(0L);
        root.setText("root");
        List<MenuVo> rootChildren = new ArrayList<>();
        Map<Long, Purview> rootPurview = ALL_LEVEL_PURVIEW.get(1);
        for(Map.Entry<Long, Purview> entry : rootPurview.entrySet()){
            Purview purview = entry.getValue();
            MenuVo menu = new MenuVo();
            menu.setId(purview.getId());
            menu.setText(purview.getPurName());
            buildChildren(menu, purview.getPurLevel(),new HashSet<>());
            rootChildren.add(menu);
        }
        root.setChildren(rootChildren);
        List<MenuVo> data = new ArrayList<>();
        data.add(root);
        resp.setData(data);
        resp.setCode(BaseEnum.SUCCESS.getCode());
        resp.setMsg("成功");
        return resp;
    }

    @Override
    public Purview getPurviewByPrimaryKey(Long id) {
        if(id ==null || id==0){
            return null;
        }
        PurviewExample example = new PurviewExample();
        PurviewExample.Criteria criteria = example.createCriteria();
        criteria.andStateEqualTo(RecordStatusConst.ENABLE);
        criteria.andIdEqualTo(id);
        Purview purview =  EntityUtil.getFirst(purviewMapper.selectByExample(example));
        return purview;
    }

    @Override
    public BaseResp doDelPurview(BaseReq request) {
        BaseResp resp = new BaseResp();
        if(request.getId()==null || request.getId()==0){
            resp.setCode(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getCode());
            resp.setMsg(BaseEnum.PURVIEW_MENU_ARGS_ERROR.getMsg());
            return resp;
        }
        PurviewExample example = new PurviewExample();
        PurviewExample.Criteria criteria = example.createCriteria();
        criteria.andParentIdEqualTo(request.getId());
        Long count = purviewMapper.countByExample(example);
        if(count!=null && count!=0){
            resp.setCode(BaseEnum.PURVIEW_MENU_HAS_CHILDREN.getCode());
            resp.setMsg(BaseEnum.PURVIEW_MENU_HAS_CHILDREN.getMsg());
            return resp;
        }
        PurviewExample remove = new PurviewExample();
        remove.createCriteria().andIdEqualTo(request.getId());
        purviewMapper.deleteByExample(remove);
        Purview purview = ALL_PURVIEW.remove(request.getId());
        if(purview!=null){
            Map<Long, Purview> level = ALL_LEVEL_PURVIEW.get(purview.getPurLevel());
            if(MapUtils.isNotEmpty(level)){
                level.remove(purview.getId());
            }
        }
        resp.setCode(BaseEnum.SUCCESS.getCode());
        resp.setMsg(BaseEnum.SUCCESS.getMsg());
        return resp;
    }

    @Override
    public boolean initPurview() {

        PurviewExample example = new PurviewExample();
        example.setOrderByClause(" pur_level asc,order_num asc ");
        List<Purview> purviewList = purviewMapper.selectByExample(example);
        if(CollectionUtils.isEmpty(purviewList)){
            return Boolean.TRUE;
        }

        for(Purview purview : purviewList){
            if(purview.getLogin()==RecordStatusConst.ENABLE && purview.getState()==RecordStatusConst.ENABLE){
                NOT_LOGIN_PUR.add(purview.getPurUrl());
            }
            Map<Long,Purview> levelPurview = ALL_LEVEL_PURVIEW.get(purview.getPurLevel());
            if(MapUtils.isEmpty(levelPurview)){
                levelPurview = new LinkedHashMap<>();
                levelPurview.put(purview.getId(),purview);
                ALL_LEVEL_PURVIEW.put(purview.getPurLevel(), levelPurview);
            }else{
                levelPurview.put(purview.getId(),purview);
            }
            ALL_PURVIEW.put(purview.getId(), purview);
        }
        return Boolean.TRUE;
    }

    @Override
    public boolean noNeedLogin(String url) {
        return NOT_LOGIN_PUR.contains(url);
    }

    @Override
    public List<PurviewVo> getPurviewByRoleIds(List<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return null;
        }
        List<PurviewVo> purUrls = purviewMapper.getPurviewByRoleIds(ids);
        return purUrls;
    }

    @Override
    public PageResp queryRolePurview(BaseReq req) {
        PageResp resp = new PageResp();
        if(req.getId()==null || req.getId()==0){
            resp.setCode(BaseEnum.PERSON_DEL_ARGS_ERROR.getCode());
            resp.setMsg(BaseEnum.PERSON_DEL_ARGS_ERROR.getMsg());
            return resp;
        }
        Set<Long> purIdSet = new HashSet<>();
        List<Long> idList = purviewMapper.selectPurIdByRoleId(req.getId());
        if(CollectionUtils.isNotEmpty(idList)){
            for(Long id : idList){
                purIdSet.add(id);
            }
        }
        List<MenuVo> data = new ArrayList<>();
        Map<Long, Purview> rootPurview = ALL_LEVEL_PURVIEW.get(1);
        for(Map.Entry<Long, Purview> entry : rootPurview.entrySet()){
            Purview purview = entry.getValue();
            MenuVo menu = new MenuVo();
            menu.setId(purview.getId());
            menu.setText(purview.getPurName());
            if(purIdSet.contains(menu.getId())){
                menu.setChecked(Boolean.TRUE);
            }else{
                menu.setChecked(Boolean.FALSE);
            }
            buildChildren(menu, purview.getPurLevel(),purIdSet);
            data.add(menu);
        }
        resp.setCode(BaseEnum.SUCCESS.getCode());
        resp.setMsg(BaseEnum.SUCCESS.getMsg());
        resp.setData(data);
        return resp;
    }

    @Override
    public PageResp<Map<String,Object>> queryMenu(List<PurviewVo> purviewList) {
        PageResp<Map<String,Object>> resp = new PageResp();
        if(CollectionUtils.isEmpty(purviewList)){
            resp.setCode(BaseEnum.PURVIEW_NOT_MATCH.getCode());
            resp.setMsg(BaseEnum.PURVIEW_NOT_MATCH.getMsg());
            return resp;
        }
        Set<String> purUrl = new HashSet<>();
        Map<String, Object> data = new HashMap<>();
        List<MenuVo> menuList = new ArrayList<>();
        for(PurviewVo purview : purviewList){
            if(purview.getLogin()==RecordStatusConst.DISABLE && purview.getPurType()==PurTypeEnum.MENU_URL.getCode() && purview.getLevel()==1){
                // 确定一级菜单
                MenuVo menu = new MenuVo();
                menu.setId(purview.getId());
                menu.setText(purview.getPurName());
                menu.setTarget(purview.getPurUrl());
                buildMenu(menu, purviewList);
                menuList.add(menu);
            }
            if(purview.getLogin()==RecordStatusConst.DISABLE){
                purUrl.add(purview.getPurUrl());
            }
        }
        data.put("purUrl", purUrl);
        data.put("menu", menuList);
        resp.setCode(BaseEnum.SUCCESS.getCode());
        resp.setData(data);
        return resp;
    }

    private void buildMenu(MenuVo root, List<PurviewVo> purviewList){
        List<MenuVo> childrenList = new ArrayList<>();
        for(PurviewVo purview : purviewList){
            if(Objects.equals(root.getId(), purview.getParentId())){
                if(purview.getPurType()==PurTypeEnum.OUT_PREFIX_API.getCode()|| purview.getPurType()==PurTypeEnum.IN_PREFIX_API.getCode() || purview.getPurType()==PurTypeEnum.MENU_URL.getCode()){
                    MenuVo children = new MenuVo();
                    children.setId(purview.getId());
                    children.setText(purview.getPurName());
                    children.setType(purview.getPurType());
                    children.setTarget(purview.getPurUrl());
                    buildMenu(children, purviewList);
                    childrenList.add(children);
                }

            }
        }
        root.setChildren(childrenList);
    }

    private void buildChildren(MenuVo root, Integer level, Set<Long> hasCheck){

        Map<Long,Purview> subLevelMap =  ALL_LEVEL_PURVIEW.get(level+1);
        if(MapUtils.isEmpty(subLevelMap)){
            root.setState("open");
            return;
        }
        Collection<Purview> subLevel = subLevelMap.values();
        if(CollectionUtils.isEmpty(subLevel)){
            root.setState("open");
            return;
        }
        List<MenuVo> childrenList = new ArrayList<>();
        for(Purview purview : subLevel){
            if(Objects.equals(root.getId(), purview.getParentId())){
                MenuVo children = new MenuVo();
                children.setId(purview.getId());
                children.setText(purview.getPurName());
                if(hasCheck.contains(children.getId())){
                    children.setChecked(Boolean.TRUE);
                }else{
                    children.setChecked(Boolean.FALSE);
                }
                buildChildren(children, purview.getPurLevel(), hasCheck);
                childrenList.add(children);
            }
        }
        if(CollectionUtils.isEmpty(childrenList)){
            root.setState("open");
        }else{
            root.setState("closed");
        }
        root.setChildren(childrenList);
    }

    private void cachePurview(Purview purview){
        Map<Long, Purview> levelPurview = ALL_LEVEL_PURVIEW.get(purview.getPurLevel());
        if(MapUtils.isEmpty(levelPurview)){
            levelPurview = new HashMap<>();
            levelPurview.put(purview.getId(),purview);
            ALL_LEVEL_PURVIEW.put(purview.getPurLevel(), levelPurview);
        }else{
            levelPurview.put(purview.getId(),purview);
        }

        ALL_PURVIEW.put(purview.getId(), purview);
    }
}
